package com.company.eos.common.util;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 *
 * @author Song, Wei
 * Date: 2022/3/18 9:28
 */
@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 锁超时时间默认值 30s
     */
    private final static Long DEFAULT_LOCK_EXPIRE = 30000L;

    /**
     * 锁阻塞时，线程睡眠时间
     */
    private final static Long DEFAULT_LOCK_WAITING = 3000L;

    /**
     * 默认锁值
     */
    private final static String DEFAULT_LOCK_VALUE = "LOCKED";

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public void setCacheObject(final String key, final Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    public void setCacheObject(final String key, final Object value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 获得缓存的基本对象
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public Object getCacheObject(final String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key 缓存键值
     */
    public Boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * setIfAbsent
     *
     * @param key     lock key
     * @param value   value
     * @param timeout timeout
     * @param unit    time unit
     * @return 设置成功返回true，设置失败返回false
     */
    public Boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
    }

    /**
     * 获取hash中的数据
     *
     * @param hashKey hash表的大key
     * @param key     hash元素的key
     * @return 数据对象
     */
    public Object hGet(String hashKey, String key) {
        return redisTemplate.opsForHash().get(hashKey, key);
    }

    /**
     * 阻塞式获取锁
     *
     * @param key 锁的键
     */
    public void lock(String key) throws InterruptedException {
        // 如果已经有线程持有锁了，就先睡眠 3 秒，再次尝试获取锁
        while (!setIfAbsent(key, DEFAULT_LOCK_VALUE, DEFAULT_LOCK_EXPIRE, TimeUnit.MILLISECONDS)) {
            Thread.sleep(DEFAULT_LOCK_WAITING);
        }
    }

    /**
     * 释放锁
     *
     * @param key 锁的键
     */
    public void unLock(String key) {
        redisTemplate.delete(key);
    }
}
